package com.config;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.converter.PageConverter;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.DefaultBaseTypeLimitingValidator;
import com.keygenerator.ArgsFieldGenerator;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
import org.springframework.cache.interceptor.*;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;

import static com.fasterxml.jackson.databind.SerializationFeature.WRAP_ROOT_VALUE;

@Configuration
public class CacheConfig {

    @Bean
    public FactoryBean<ObjectMapper> objectMapperFactoryBean() {


        return new Jackson2ObjectMapperFactoryBean();
    }

    /**
     * 注册 缓存切面
     *
     * @return
     */
    @Bean
    public BeanFactoryCacheOperationSourceAdvisor cacheOperationSourceAdvisor(CacheInterceptor cacheInterceptor) {

        final BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();

        // 具体执行缓存操作的切面方法
        advisor.setAdvice(cacheInterceptor);

        // 用于读取切面注解 @Cacheable 等 来获取该如何进行缓存操作
        // 缓存的key是什么，什么情况下缓存等
        advisor.setCacheOperationSource(cacheOperationSource());

        return advisor;
    }

    @Bean
    public AnnotationCacheOperationSource cacheOperationSource() {
        return new AnnotationCacheOperationSource();
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration("localhost", 6379);
        standaloneConfig.setPassword("lq318925");
        standaloneConfig.setDatabase(0);

        return new JedisConnectionFactory(standaloneConfig);
    }

    @Bean
    public CacheManager cacheManager() {

        final ApplicationConversionService conversionService = new ApplicationConversionService();

        conversionService.addConverter(new PageConverter());



        /**
         * 用于 Redis json 序列化和反序列化的 ObjectMapper 还是单独用一个为好
         * 因为序列化和反序列化要记录 class 路径信息，这一点对于前后端json通信来说是不必要的
         */
        ObjectMapper objectMapper = new ObjectMapper();
        // 序列化时开启 写入 class 路径信息
        final BasicPolymorphicTypeValidator validator = BasicPolymorphicTypeValidator
                .builder()
                // 只要是 Object 的子类就都能通过多态校验
                .allowIfBaseType(Object.class)
                .build();
        objectMapper.activateDefaultTyping(validator,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        final Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        jsonRedisSerializer.setObjectMapper(objectMapper);

        final RedisSerializationContext.SerializationPair<Object> objectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 用于把方法参数中的特殊对象转换为缓存的key
                .withConversionService(conversionService)
                .serializeValuesWith(objectSerializationPair)
                /*.entryTtl()*/;


        return RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(
                        redisConnectionFactory()
                )
                .cacheDefaults(config)
                .build();

    }

    @Bean
    public CacheResolver cacheResolver(CacheManager cacheManager) {
        return new SimpleCacheResolver(cacheManager);
    }

    @Bean
    public CacheInterceptor cacheInterceptor(CacheResolver cacheResolver) {
        final CacheInterceptor cacheInterceptor = new CacheInterceptor();
        cacheInterceptor.setCacheOperationSources(cacheOperationSource());
        cacheInterceptor.setCacheResolver(cacheResolver);
        return cacheInterceptor;
    }

    @Bean
    public KeyGenerator argsFieldGenerator(){
        return new ArgsFieldGenerator();

    }


}
